#ifndef esda_Timestamp_h
#define esda_Timestamp_h

/**
 * esda/core/Timestamp.H --
 *
 * Copyright (C) Olivier Le Floch
 *
 * See the file LICENSE for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "common.h"

#include <sys/time.h>
#include <time.h>

namespace esda
{
	class Timestamp {

	public:

	/**
	 * This class is used to represent points in time.
	 * 
	 * The date+time information is stored as a single 64-bit integer
	 * with millisecond precision, which makes it easy to compute
	 * durations. It can also be converted to/from a UTC ISO 8601
	 * string-based representation.
	 * 
	 * Timestamps are described as the number of milliseconds elapsed
	 * since 1970-01-01T00:00:00.000Z. The minimum date+time that can
	 * be represented is 1901-12-13T20:45:52Z. The maximum one is
	 * 2038-01-19T03:14:07Z. These values were chosen to remain
	 * compatible with POSIX 32-bit based time_t representations.
	 *
	 * Useful links: 
	 *		 - http://en.wikipedia.org/wiki/Unix_time
	 *		 - http://en.wikipedia.org/wiki/ISO_8601
	 */

		typedef int64_t inttype;
	
		static inttype undef, min, max;

	protected:

		inttype milliseconds;
	
		static inttype now(void);
	
		static inttype string2int(string s);
		static string int2string(inttype i);
	
		static inttype ext2int(int year, int month, int day, 
			int hour, int mini, int sec, int msec);
		static void int2ext(inttype t, 
			int *year, int *month, int *day, 
			int *hour, int *mini, int *sec, int *msec, 
			int *wday, int *yday, bool utc);

	public:

		Timestamp(void) : milliseconds(now()) {}
		Timestamp(const Timestamp &s) : milliseconds(s.milliseconds) {}

		// All the specified times are assumed to be UTC-based
		Timestamp(inttype ms);
		Timestamp(string s) : milliseconds(string2int(s)) {}

		Timestamp(int year, int month, int day, int hour, int mini, int sec, int msec) : milliseconds(ext2int(year, month, day, hour, mini, sec, msec)) {}

		bool operator<(const Timestamp &other) const {
			return milliseconds < other.milliseconds;
		}

		bool operator>(const Timestamp &other) const {
			return milliseconds > other.milliseconds;
		}

		bool operator<=(const Timestamp &other) const {
			return milliseconds <= other.milliseconds;
		}

		bool operator>=(const Timestamp &other) const {
			return milliseconds >= other.milliseconds;
		}

		Timestamp &operator=(const Timestamp::inttype t);

		bool operator==(const Timestamp &other) const {
			return milliseconds == other.milliseconds;
		}

		bool operator==(const Timestamp::inttype t) const {
			return milliseconds == t;
		}

		Timestamp::inttype operator-(const Timestamp &other) const {
			return milliseconds - other.milliseconds;
		}

		Timestamp::inttype operator+(const Timestamp &other) const {
			return milliseconds+other.milliseconds;
		}

		Timestamp& operator++() {
			milliseconds++;
			
			return *this;
		}
		
		Timestamp::inttype operator-(const Timestamp::inttype t) const {
			return milliseconds - t;
		}

		Timestamp::inttype operator+(const Timestamp::inttype t) const {
			return milliseconds + t;
		}

		// the number of milliseconds since 1970-01-01T00:00:00.000Z
		inttype toInt(void) const {
			return milliseconds; 
		}

		// a UTC ISO 8601 combined data/time string (e.g. 2006-02-25T11:59:12.113Z)
		string toString(void) const {
			return int2string(milliseconds); 
		}
		
		friend ostream& operator<<(ostream& o, const Timestamp& t) {
			return o << t.toString();
		};
		
		friend string operator+(string s, const Timestamp& t) {
			stringstream strstream;
			
			(strstream << s) << t.milliseconds;
			
			return strstream.str();
		}
		
		// Use 0 for components you don't want
		void getAsUTCTime(
			int *year, int *month, int *day,
			int *hour, int *mini, int *sec, int *msec,
			int *wday = 0, int *yday = 0)
		{
			return int2ext(milliseconds, 
				year, month, day, 
				hour, mini, sec, msec, 
				wday, yday,
				true);
		}

		// Use 0 for components you don't want
		void getAsLocalTime(
			int *year, int *month, int *day,
			int *hour, int *mini, int *sec, int *msec,
			int *wday = 0, int *yday = 0)
		{
			return int2ext(milliseconds, 
				year, month, day,
				hour, mini, sec, msec, 
				wday, yday, 
				false);
		}

		static inttype getLocalUTCOffset(void);

		static inttype createAsInt(void) {
			return now();
		}
	
		static inttype createAsIntFrom(string s) {
			return string2int(s);
		}
	
		static inttype createAsIntFrom(
			int year, int month, int day,
			int hour, int mini, int sec, int msec)
		{
			return ext2int(year,month,day,hour,mini,sec,msec);
		}

		static string createAsString(void) {
			return int2string(now());
		}
	
		static string createAsStringFrom(inttype i) {
			return int2string(i);
		}
	
		static string createAsStringFrom(
			int year, int month, int day,
			int hour, int mini, int sec, int msec)
		{
			return int2string(ext2int(year,month,day,hour,mini,sec,msec));
		}
	
		// Deprecated syntax...
		static inttype createAsIntFromString(string s) {
			return string2int(s);
		}
	
		static string createAsStringFromInt(inttype i) {
			return int2string(i);
		}
	};
}

#endif
